// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef CLOUDAPPSDK_CASCONTROLLRT_H
#define CLOUDAPPSDK_CASCONTROLLRT_H

#include <android/native_window.h>
#include <EGL/eglplatform.h>
#include <mutex>
#include "CasDataPipe.h"
#include "CasTouch.h"
#include "CasConf.h"
#include "CasGadget.h"
#include "CasMsg.h"
#include "CasSocket.h"
#include "CasAppCtrlCmdUtils.h"
#include "CasCmdController.h"
#include "CasCmdControlThread.h"
#include "CasHeartbeatThread.h"
#include "CasHeartbeatController.h"
#include "CasVideoHDecodeThread.h"
#include "CasStreamRecvParser.h"
#include "CasStreamBuildSender.h"

class CasController : public CasControllerListener {
public:
    static CasController *GetInstance();

    static bool DestroyInstance();

    CasController();

    ~CasController();

    bool Start(ANativeWindow *nativeWindow, bool isHome);

    bool Stop(bool isHome);

    bool Reconnect();

    bool SetMediaConfig(std::map<std::string, std::string> mediaConfig);

    void SetJniConf(std::string key, std::string value);

    bool SendTouchEvent(int id, int action, int x, int y, int pressure, long time, int orientation, int height, int width);

    bool SendKeyEvent(uint16_t keycode, uint16_t action);

    bool SendMotionEvent(uint16_t masterAxis, int32_t masterValue, uint16_t secondaryAxis, int32_t secondaryValue);

    void SetCmdCallBackMethod(void *(*method)(int type, std::string msg))
    {
        cmdCallBack = method;
    }

    int JniSendData(CasMsgType type, uint8_t *data, int length);

    int JniRecvData(CasMsgType type, uint8_t *data, int length);

    uint64_t GetLag();

    JNIState GetState();

    void SetState(JNIState state);

    bool GetConnectStatus();

    void NotifyFirstVideoFrame();

private:
    bool Release();

    void IsNeedRotation(int orientation);

    void ResetDecoder(bool isClearStream);

    bool SendStartCmd();

    bool CreateWorkers();

    bool StartWorkers();

    bool DestroyWorkers();

    bool CreateDecWorker(ANativeWindow *nativeWindow, bool needVideoDecode);

    void StartDecWorker(bool retainVideoDecode);

    void StopDecWorker(bool retainVideoDecode);

    bool InitDataStream();

    bool ClearDataStream();

    bool CloseDataStream();

    bool BuildConnection();

    bool SendCommand(std::map<std::string, std::string> parameters);

    void ProcessEnterBackground();

    bool ProcessEnterForeground(ANativeWindow *nativeWindow);

    bool ForceIFrame();

    void NotifyCommand(int type, std::string msg);

    void OnCmdRecv(int code, std::string msg);

    bool IsValidMediaConfig(std::map<std::string, std::string> mediaConfig);

    std::string CalcMaxDisconnectDuration(std::string backgroundTimeout);

    void *(*cmdCallBack)(int type, std::string msg) = nullptr;

    static CasController *g_instance;
    CasDataPipe *m_videoPacketStream = nullptr;
    CasDataPipe *m_audioPacketStream = nullptr;
    CasDataPipe *m_orientationStream = nullptr;
    CasDataPipe *m_controlStream = nullptr;
    CasDataPipe *m_channelStream = nullptr;
    CasDataPipe *m_virtualDeviceStream = nullptr;
    CasDataPipe *m_imeDataStream = nullptr;

    CasCmdController *m_cmdController = nullptr;
    CasHeartbeatController *m_heartbeatController = nullptr;
    CasStreamParseThread *m_streamParseThread = nullptr;
    CasCmdControlThread *m_controlThread = nullptr;
    CasHeartbeatThread *m_heartbeatThread = nullptr;
    CasSocket *m_casClientSocket = nullptr;
    CasStreamBuildSender *m_streamBuildSender = nullptr;
    CasStreamRecvParser *m_streamParser = nullptr;
    CasVideoHDecodeThread *m_videoDecodeThread = nullptr;

    std::map<std::string, std::string> m_jniConf;
    enum JNIState m_state = INIT;
    CasTouch *m_touch = nullptr;
    CasConf m_conf;
    std::mutex m_jniConfLock;
    std::mutex m_lock;
    std::mutex m_callbackLock;
    std::mutex m_decoderLock;
    std::string m_sessionId;
    std::string m_ticket;
    std::string m_encryptedData;
    std::string m_verifyData;
    std::string m_authTs;
    std::string m_aesIv;
    std::string m_clientType;
    std::string m_maxDisconnectDuration;
    FrameType m_frameType;
    ANativeWindow *m_nativeWindow;
    unsigned int m_ip = 0;
    unsigned short m_port = 0;
    const bool m_retainVideoDecode = true;
    const bool m_needVideoDecode = true;
    bool m_isNotifyFirstFrame = false;
    std::map<std::string, std::string> m_mediaConfig;
    int m_orientation = 0;
    int m_rotationDegrees = 0;
};

#endif // CLOUDAPPSDK_CASCONTROLLRT_H